package com.example.blog.utils;

import com.example.blog.domain.*;
import com.example.blog.exception.BusinessException;
import com.example.blog.dto.writing.ArticleLikeDto;
import com.example.blog.dto.comments.CommunicationSendDto;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.TimeUnit;

@Service
@Transactional
@Slf4j
public class RedisStorage {


    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    //注入序列化器
    @Resource
    private ObjectMapper mappers;

    private static final StringBuffer stringBuffer = new StringBuffer();
    // 记录用户登录状态信息的前缀
    private static final String LOGIN_USER = "loginUser:";
    //存储用户消息的前缀
    private static final String USER_MESSAGE = "communication:";
    //存储用户点赞信息的前缀
    private static final String SUPPORT = "support:";

    //签到表的前缀
    private static final String SIGN_IN = "signIn:";

    //权限的前缀
    private static final String PERMISSIONS = "Permissions:";

    //验证码
    private static final String CAPTCHA = "captcha_:";

    //IP记录前缀
    private static final String IP = "ip_access:";

    //写入用户的登录token作为唯一用户标识
    public boolean recordLogins(@NotNull LoginUser user) throws JsonProcessingException {
        if (user.getId() == 0 || (Objects.equals(user.getToken(), "")) || user.getToken() == null)
            return false;
        //创建序列化器
//        ObjectMapper objectMapper1  = new ObjectMapper();
        //手动序列化用户的信息
        String token = mappers.writeValueAsString(user);
        //存入数据
        stringRedisTemplate.opsForValue().set(LOGIN_USER.concat(String.valueOf(user.getId())), token);
        //读取数据
//        String str = stringRedisTemplate.opsForValue().get(LOGIN_USER.concat(String.valueOf(user.getId())));
        return true;
    }


    //检查是用户的登录状态，token是否正确
    public boolean checkLogin(LoginUser user) throws JsonProcessingException {
        String str;
        //取出数据
        try {
            str = stringRedisTemplate.opsForValue().get(LOGIN_USER.concat(String.valueOf(user.getId())));
        } catch (Exception e) {
            return false;
        }
        //手动反序列化
        if (str == null) {
            return false;
        }
        LoginUser users = mappers.readValue(str, LoginUser.class);
        //返回结果，结果为真则用户正常登录，否则用户登录在了别处
        return user.getToken().equals(users.getToken());
    }

    //退出登录状态,
    public Boolean SignOut(String id){
        stringRedisTemplate.delete(PERMISSIONS.concat(id));
        stringRedisTemplate.delete(LOGIN_USER.concat(id));
        return false;
    }


    //存入信息,返回信息数
    public Long communicationSet(List<CommunicationSendDto> user) throws JsonProcessingException {
        List<String> info = new ArrayList<>();
        for (CommunicationSendDto c : user) {
            info.add(mappers.writeValueAsString(c));
        }
        return stringRedisTemplate.opsForList().rightPushAll(USER_MESSAGE.concat(String.valueOf(user.get(0).getRecipient())), info);
    }


    //取出信息信息,
    public List<Communication> communicationGet(long id) throws JsonProcessingException {
        if (id <= 0)
            return null;
        List<String> info = new ArrayList<>();
        List<Communication> communication = new ArrayList<>();
        //取出信息
        long index = stringRedisTemplate.opsForList().size(USER_MESSAGE.concat(String.valueOf(id)));
        for (long i = 0; i < index; i++)
            info.add(stringRedisTemplate.opsForList().leftPop(USER_MESSAGE.concat(String.valueOf(id)), 1, TimeUnit.SECONDS));
//        //反序列化
        for (String str : info)
            communication.add(mappers.readValue(str, Communication.class));
        return communication;
    }


    //检查是该文章是否被点赞
    public boolean supportStatue(ArticleLikeDto writings) throws JsonProcessingException {
        return Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember(SUPPORT.concat(String.valueOf(writings.getUid())), writings.getWid()));
    }

    //存入或删除点赞信息
    public long supportChoose(ArticleLikeDto writings) throws JsonProcessingException {
        if (Boolean.TRUE.equals(stringRedisTemplate.opsForSet().isMember(SUPPORT.concat(String.valueOf(writings.getUid())), writings.getWid())))
            return stringRedisTemplate.opsForSet().remove(SUPPORT.concat(String.valueOf(writings.getUid())), writings.getWid());
        else
            return stringRedisTemplate.opsForSet().add(SUPPORT.concat(String.valueOf(writings.getUid())), writings.getWid());
    }

    //检查用户是否已经登录
    public boolean checkSingInStatue(long uid) {
        return stringRedisTemplate.hasKey(SIGN_IN.concat(String.valueOf(uid)));
    }


    //记录用户签到信息
    public boolean insertSingIn(SignIn sign) {
        stringRedisTemplate.opsForValue().set(SIGN_IN.concat(String.valueOf(sign.getUid())), sign.getDateTime(), sign.getSeconds(), TimeUnit.SECONDS);
        return false;
    }

    //存入验证码
    public void insertCaptcha(String name, String captcha, long seconds) {
        stringRedisTemplate.opsForValue().set(CAPTCHA.concat(name), captcha, seconds, TimeUnit.SECONDS);
    }


    //删除验证码
    public boolean deleteCaptcha(String key) {
        return Boolean.TRUE.equals(stringRedisTemplate.delete(CAPTCHA.concat(key)));
    }

    //检查验证码
    public boolean checkEnrollCaptcha(String key, String captcha) {
        String keys = CAPTCHA.concat(key);
        if (Boolean.TRUE.equals(stringRedisTemplate.hasKey(keys))) {
            if (Objects.equals(stringRedisTemplate.opsForValue().get(keys), captcha)) {
                stringRedisTemplate.delete(keys);
                return true;
            } else {
                return false;
            }
        }
        return false;
    }


    //检查权限权限
    public boolean checkPermission(long uid, String path) throws JsonProcessingException {
        String permissionStr = stringRedisTemplate.opsForValue().get(PERMISSIONS.concat(String.valueOf(uid)));
        permissionStr = permissionStr.substring(1, permissionStr.length() - 1);
        List<String> permissions = Arrays.asList(permissionStr.split(", "));
        for (String s : permissions) {
            if (path.equals(s)) {
                return true;
            }
        }
        return false;
    }

    //存入权限
    public void setPermission(Collection<? extends GrantedAuthority> permission, long uid) throws JsonProcessingException {
        stringRedisTemplate.opsForValue().set(PERMISSIONS.concat(String.valueOf(uid)), String.valueOf(permission));
    }
    public void setPermission(List<String> permission, long uid) throws JsonProcessingException {
        stringRedisTemplate.opsForValue().set(PERMISSIONS.concat(String.valueOf(uid)), String.valueOf(permission));
    }
    //檢查IP访问
    public void checkIpAccess(String ip) {
        if (stringRedisTemplate.opsForValue().get(IP.concat(ip)) != null) {
            String visitsCount = stringRedisTemplate.opsForValue().get(IP.concat(ip));
            if(visitsCount == null){
                visitsCount = "1";
            }
            int count = Integer.parseInt(visitsCount); //获取次數
            if (count == 200) {
                stringRedisTemplate.opsForValue().set(IP.concat(ip), String.valueOf(Integer.parseInt(visitsCount) + 1), 30, TimeUnit.MINUTES);
            } else if (count > 200) {
                throw new BusinessException(403, "IP已被暂时封禁");
            } else {
                stringRedisTemplate.opsForValue().set(IP.concat(ip), String.valueOf(Integer.parseInt(visitsCount) + 1), 2, TimeUnit.SECONDS);
            }
        } else {
            stringRedisTemplate.opsForValue().set(IP.concat(ip), String.valueOf(1), 2, TimeUnit.SECONDS);
        }
    }


}
